package utils

import (
	"runtime"
	"sync"
	"sync/atomic"
)

type MemBlock struct {
	write_idx uint16
	read_idx  uint16
	data      []byte
	tag       interface{}
	ownerPool *MemBlockPool
}

func (this *MemBlock) Release2Pool() {
	if this.ownerPool != nil {
		this.tag = nil
		this.ownerPool.PutBack(this)
	}
}

func (this *MemBlock) ReleaseAuto() {
	if this.ownerPool != nil {
		this.tag = nil
		this.ownerPool.PutBack(this)
	} else {
		this.tag = nil
	}
}

func (this *MemBlock) GetBytes() []byte {
	return this.data[:this.write_idx]
}

func (this *MemBlock) Write(buf []byte) (n int, err error) {
	n = copy(this.data[this.write_idx:], buf)
	this.write_idx += uint16(n)
	return
}

type MemBlockPool struct {
	block_cnt  int32
	block_size uint16
	pool       *sync.Pool
}

func (this *MemBlockPool) NewFunc() interface{} {
	blk := &MemBlock{}
	atomic.AddInt32(&this.block_cnt, 1)
	runtime.SetFinalizer(blk, func(obj interface{}) {
		atomic.AddInt32(&this.block_cnt, -1)
	})
	blk.read_idx = 0
	blk.write_idx = 0
	blk.data = make([]byte, this.block_size)
	blk.ownerPool = this
	return blk
}

func NewMemBlockPool(size uint16) *MemBlockPool {
	rval := &MemBlockPool{
		block_size: size,
		pool:       &sync.Pool{},
	}
	rval.pool.New = rval.NewFunc
	return rval
}

func (this *MemBlockPool) GetBlockAliveNum() int32 {
	return this.block_cnt
}

func (this *MemBlockPool) GetBlock() *MemBlock {
	rval := this.pool.Get().(*MemBlock)
	rval.tag = nil
	rval.read_idx = 0
	rval.write_idx = 0
	return rval
}

func (this *MemBlockPool) PutBack(mem *MemBlock) {
	if len(mem.data) != int(this.block_size) {
		mem.ownerPool = nil // 直接释放
		mem.data = nil
		return
	}
	this.pool.Put(mem)
}

func (this *MemBlockPool) ConfigMemBlockSize(size uint16) {
	this.block_size = size
}
